package com.jzo2o.market.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.utils.*;
import com.jzo2o.market.constants.RedisConstants;
import com.jzo2o.market.constants.TabTypeConstants;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.mapper.ActivityMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.dto.request.ActivityQueryForPageReqDTO;
import com.jzo2o.market.model.dto.request.ActivitySaveReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.model.dto.response.ActivityQueryForPageResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.market.service.ICouponWriteOffService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.jzo2o.market.enums.ActivityStatusEnum.*;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-09-16
 */
@Service
public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity> implements IActivityService {
    private static final int MILLION = 1000000;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ICouponService couponService;

    @Resource
    private ICouponWriteOffService couponWriteOffService;


    /**
     * 新增和修改优惠卷活动
     *
     * @param dto
     */
    @Override
    public void creatAndUpdateCoupon(ActivitySaveReqDTO dto) {
        if (dto == null) {
            throw new RuntimeException("参数为空");
        }
        //检查参数
        dto.check();
        Long id = dto.getId();

        Activity activity = BeanUtils.copyBean(dto, Activity.class);
        activity.setStockNum(dto.getTotalNum());
        if (ObjectUtils.isEmpty(id)) {
            //新增
            activity.setId(IdUtils.getSnowflakeNextId());
            activity.setStatus(NO_DISTRIBUTE.getStatus());
            boolean save = this.save(activity);
            if (!save) {
                throw new DBException("新增活动失败");
            }
        } else {
            //修改
            boolean update = this.updateById(activity);
            if (!update) {
                throw new DBException("修改活动失败");
            }
        }
    }


    /**
     * 分页查询优惠卷活动
     *
     * @param dto
     * @return
     */
    @Override
    public ActivityQueryForPageResDTO queryPageActivity(ActivityQueryForPageReqDTO dto) {
        if (dto == null) {
            throw new ForbiddenOperationException("参数为空");
        }
        Page page = new Page(dto.getPageNo(), dto.getPageSize());
        Page pageQuery = this.lambdaQuery()
                .eq(ObjectUtils.isNotEmpty(dto.getId()), Activity::getId, dto.getId())
                .eq(ObjectUtils.isNotEmpty(dto.getName()), Activity::getName, dto.getName())
                .eq(ObjectUtils.isNotEmpty(dto.getType()), Activity::getType, dto.getType())
                .eq(ObjectUtils.isNotEmpty(dto.getStatus()), Activity::getStatus, dto.getStatus())
                .orderByDesc(Activity::getCreateTime)
                .page(page);

        List<ActivityInfoResDTO> activityInfoResDTOS = BeanUtils.copyToList(pageQuery.getRecords(), ActivityInfoResDTO.class);
        ActivityQueryForPageResDTO resDTO = new ActivityQueryForPageResDTO();
        resDTO.setList(activityInfoResDTOS);
        resDTO.setTotal(pageQuery.getTotal());
        resDTO.setPages(pageQuery.getPages());

        return resDTO;
    }

    /**
     * 撤销优惠卷活动
     *
     * @param id 活动id
     */
    @Override
    @Transactional
    public void revoke(Long id) {
        //活动是否存在
        Activity activity = this.getById(id);
        if (ObjectUtils.isEmpty(activity)) {
            throw new ForbiddenOperationException("活动不存在");
        }
        //判断活动状态是否为进行中或待发布
        if (ObjectUtils.notEqual(activity.getStatus(), NO_DISTRIBUTE.getStatus())
                && ObjectUtils.notEqual(activity.getStatus(), DISTRIBUTING.getStatus())) {
            throw new ForbiddenOperationException("活动时间已到或已撤销");
        }
        //修改活动状态为撤销
        boolean update = this.lambdaUpdate()
                .eq(Activity::getId, id)
                .set(Activity::getStatus, ActivityStatusEnum.VOIDED.getStatus())
                .update();
        if (!update) {
            throw new DBException("撤销活动失败");
        }

        //根据活动id 修改优惠卷状态未使用的为作废
        boolean update1 = couponService.lambdaUpdate()
                .eq(Coupon::getActivityId, id)
                .eq(Coupon::getStatus, CouponStatusEnum.NO_USE.getStatus())
                .set(Coupon::getStatus, CouponStatusEnum.VOIDED.getStatus())
                .update();
        if (!update1) {
            throw new DBException("修改优惠卷状态失败");
        }

    }

    /**
     * 预热活动列表
     */
    @Override
    public void Preheat() {
        //预热活动列表
        List<Activity> activityList = this.lambdaQuery()
                .in(Activity::getStatus, NO_DISTRIBUTE.getStatus(), DISTRIBUTING.getStatus())
                .lt(Activity::getDistributeStartTime, DateUtils.now().plusDays(30))
                .orderByAsc(Activity::getDistributeStartTime)
                .list();

        if (ObjectUtils.isEmpty(activityList)) {
            activityList = new ArrayList<>();
        }
        List<ActivityInfoResDTO> activityInfoResDTOS = BeanUtils.copyToList(activityList, ActivityInfoResDTO.class);
        String jsonString = JSON.toJSONString(activityInfoResDTOS);

        String key = RedisConstants.RedisKey.ACTIVITY_CACHE_LIST;

        redisTemplate.opsForValue().set(key, jsonString);

        //预热库存
        //1. 待开始的活动，所有活动的库存信息全部预热到缓存
        activityInfoResDTOS.stream()
                .filter(x -> ObjectUtils.equals(x.getStatus(), NO_DISTRIBUTE.getStatus()))
                .forEach(x -> {
                    String redisKeyStock = String.format(RedisConstants.RedisKey.COUPON_RESOURCE_STOCK, x.getId() % 10);
                    redisTemplate.opsForHash().put(redisKeyStock, x.getId(), x.getStockNum());
                });

        //2. 进行中的活动，缓存中没数据时才预热到缓存
        activityInfoResDTOS.stream()
                .filter(x -> ObjectUtils.equals(x.getStatus(), DISTRIBUTING.getStatus()))
                .forEach(x -> {
                    String redisKeyStock = String.format(RedisConstants.RedisKey.COUPON_RESOURCE_STOCK, x.getId() % 10);
                    redisTemplate.opsForHash().putIfAbsent(redisKeyStock, x.getId(), x.getStockNum());
                });


    }

    /**
     * 用户端抢券活动列表两种状态的分页查询
     *
     * @param tabType
     * @return
     */
    @Override
    public List<ActivityInfoResDTO> queryForPage(Integer tabType) {
        Object json = redisTemplate.opsForValue().get(RedisConstants.RedisKey.ACTIVITY_CACHE_LIST);
        List<ActivityInfoResDTO> list = JsonUtils.toList(json.toString(), ActivityInfoResDTO.class);

        //3.将用户选中的类型翻译成想要的活动状态
        Integer selectStatus = ObjectUtils.equals(tabType, TabTypeConstants.SEIZING) ? DISTRIBUTING.getStatus() : NO_DISTRIBUTE.getStatus();

        List<ActivityInfoResDTO> collect = list.stream().filter(x -> ObjectUtils.equal(selectStatus, getRealStatus(x)))
                .peek(x -> {
                    x.setRemainNum(x.getStockNum()); //优惠券活动剩余数量等于库存数量
                    x.setStatus(selectStatus);
                })
                .collect(Collectors.toList());

        return collect;
    }

    /**
     * 获取真实状态
     *
     * @param x
     * @return
     */
    public Integer getRealStatus(ActivityInfoResDTO x) {
        Integer status = x.getStatus();
        LocalDateTime start = x.getDistributeStartTime();
        LocalDateTime end = x.getDistributeEndTime();

        //1. 未开始的活动 - 如果活动时间已到，处理成进行中
        if (ObjectUtils.equal(status, NO_DISTRIBUTE.getStatus()) && DateUtils.now().isBefore(start)) {
            x.setStatus(DISTRIBUTING.getStatus());
        } else if (ObjectUtils.equal(status, NO_DISTRIBUTE.getStatus()) && DateUtils.now().isAfter(end)) {
            //2. 未开始的活动 - 如果活动时间已过，处理成已过期
            x.setStatus(LOSE_EFFICACY.getStatus());
        } else if (ObjectUtils.equal(status, DISTRIBUTING.getStatus()) && DateUtils.now().isAfter(end)) {
            //3. 进行中的活动 - 如果活动时间已过，处理成已过期
            x.setStatus(LOSE_EFFICACY.getStatus());
        }

        return x.getStatus();
    }

    /**
     * 扣减库存 减1
     *
     * @param activityId
     * @return
     */
    @Override
    public Boolean decrStock(Long activityId) {
        boolean update = this.lambdaUpdate()
                .eq(Activity::getId, activityId)
                .gt(Activity::getStockNum, 0)
                .setSql("stock_num = stock_num - 1")
                .update();
        return update;
    }
}
